﻿using Bing;
using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using SimpleLiteForm;
using System.Globalization;

namespace SimpleLiteForm.References
{
    /// <summary>
    /// The Bing Search reference service.
    /// </summary>
    class BingReferenceService : IReferenceService
    {
        /// <summary>
        /// The singleton instance.
        /// </summary>
        private static IReferenceService _instance;

        /// <summary>
        /// This is the service root uri for the Microsoft Bing Search Web-only service.
        /// </summary>
        private readonly Uri SERVICE_ROOT_URI = new Uri("https://api.datamarket.azure.com/Bing/Search");

        /// <summary>
        /// This is the Account Key I generated for this app.
        /// </summary>
        private const string ACCOUNT_KEY = "xUVV+Z3Qe+HicbLBQz2wGd0DgBk+tpeFJ0ROrXbm8NE=";

        /// <summary>
        /// The bing search container.
        /// </summary>
        private BingSearchContainer _container;

        /// <summary>
        /// The last search results.
        /// </summary>
        private IList<ReferenceResult> _results = new List<ReferenceResult>();

        /// <summary>
        /// The reference service cache. The key looks like language->translatedword, e.g. en->optimization (eveything in lower case).
        /// </summary>
        private Dictionary<string, IList<ReferenceResult>> _cache = new Dictionary<string, IList<ReferenceResult>>();

        /// <summary>
        /// Creates a new BingReferenceService instance.
        /// </summary>
        private BingReferenceService()
        {
            _container = new BingSearchContainer(SERVICE_ROOT_URI);
            _container.IgnoreResourceNotFoundException = true;

            // Configure bingContainer to use your credentials.
            _container.Credentials = new NetworkCredential(ACCOUNT_KEY, ACCOUNT_KEY);
        }

        /// <summary>
        /// Searches for a term asynchronously.
        /// </summary>
        /// <param name="term">The term.</param>
        /// <param name="language">The language.</param>
        public void Search(string term, CultureInfo language)
        {
            string key = string.Format("{0}->{1}", language.Name.ToLowerInvariant(), term.ToLowerInvariant());

            // check cache
            if (_cache.ContainsKey(key))
            {
                _results = _cache[key];
                return;
            }

            DataServiceQuery<WebResult> webQuery = _container.Web(term, language.Name, null, null, null, null, null, null);
            webQuery = webQuery.AddQueryOption("$top", 5);

            IEnumerable<WebResult> webResults = webQuery.Execute();

            // create a new list
            // note: do not delete the last one, because it is stored in the cache
            _results = new List<ReferenceResult>();

            foreach (var result in webResults)
            {
                _results.Add(new ReferenceResult(result.Title, result.Description, new Uri(result.Url, UriKind.Absolute)));
                Debug.WriteLine(string.Format("[BING result] {0} : {1}", result.Title, result.Description));
            }

            // add a copy to the cache
            _cache.Add(key, _results);
        }

        /// <summary>
        /// Clears the cache.
        /// </summary>
        public void ClearCache()
        {
            _cache.Clear();
        }

        /// <summary>
        /// Gets the bing reference service instance.
        /// </summary>
        public static IReferenceService Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new BingReferenceService();

                return _instance;
            }
        }

        /// <summary>
        /// Gets wheter there is a search result. This may take a while
        /// because the serach is done asynchronously.
        /// </summary>
        public bool HasResult
        {
            get
            {
                return _results.Count > 0;
            }
        }

        /// <summary>
        /// Gets wheter the async search is still going on.
        /// </summary>
        public bool IsLoading
        {
            get
            { 
                return true; 
            }
        }

        /// <summary>
        /// Gets the search result.
        /// </summary>
        public IList<ReferenceResult> Results
        {
            get
            {
                return _results;
            }
        }
    }
}
